package com.bigknow.appstore.web.base.service.impl;

import com.bigknow.appstore.web.base.dao.IRoleDao;
import com.bigknow.appstore.web.base.dao.IUserDao;
import com.bigknow.appstore.web.base.service.IPermissionService;
import com.bigknow.appstore.web.base.service.ServiceUtil;
import com.bigknow.appstore.web.framework.service.BaseCRUDService;
import com.bigknow.appstore.web.framework.util.GlobalConstant;
import com.bigknow.appstore.common.AppStoreRuntimeException;
import com.bigknow.appstore.common.util.StringUtils;
import com.bigknow.appstore.web.base.dao.IPermissionDao;
import com.bigknow.appstore.web.base.entity.Permission;
import com.bigknow.appstore.web.base.entity.Role;
import com.bigknow.appstore.web.base.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by TwelveFoldEr on 2016/3/11.
 */
@Service
public class PermissionService extends BaseCRUDService<Permission, IPermissionDao> implements IPermissionService {

    @Autowired
    private IRoleDao roleDao;
    @Autowired
    private IUserDao userDao;

    @Override
    public int update(Permission entity) {
        if (StringUtils.isBlank(entity.getId())) {
            return 0;
        } else {
            // In updating, if code change then must update children permission's parentCode.
            Permission old = this.dao.get(entity.getId());
            if (!old.getCode().equals(entity.getCode())) {
                List<Permission> permissionList = findChildren(old);
                permissionList.forEach(permission -> {
                    permission.setParentCode(entity.getCode());
                    super.save(permission);
                });
            }
            return super.save(entity);
        }
    }

    /**
     * Cannot delete permission when role has associated or has children permission.
     *
     * @param id
     * @return
     */
    @Override
    @Transactional
    public int delete(String id) {
        Permission permission = this.dao.get(id);
        if (permission == null) return 0;

        Role role = new Role();
        role.setPermissionIds(id);
        List<Role> roleList = this.roleDao.findWithFuzzy(role);
        if (!roleList.isEmpty()) {
            // strict verify.
            roleList.forEach(r -> {
                String ids = r.getPermissionIds() + ",";
                if (ids.indexOf(id + ",") >= 0) {
                    throw new AppStoreRuntimeException(
                            springUtils.getMessage(GlobalConstant.ERROR_DELETE_PERMISSION_ROLE_EXIST));
                }
            });
        }

        List<Permission> permissionList = findChildren(permission);
        if (!permissionList.isEmpty())
            throw new AppStoreRuntimeException(
                    springUtils.getMessage(GlobalConstant.ERROR_DELETE_PERMISSION_CHILD_EXIST));

        return this.dao.delete(id);
    }

    private List<Permission> findChildren(Permission permission) {
        Permission child = new Permission();
        child.setParentCode(permission.getCode());
        return this.dao.find(child);
    }

    public List<Permission> findByRoleId(String... ids) {
        List<Role> roles = this.roleDao.findById(ids);
        StringBuilder roleIds = new StringBuilder();
        roles.forEach(role -> {
            roleIds.append(role.getPermissionIds()).append(GlobalConstant.COMBINATION_SEPARATOR);
        });

        return this.dao.findById(roleIds.toString());
    }

    @Override
    public List<Permission> getTree() {
        return toTree(this.dao.findAll());
    }

    private List<Permission> getPermissionsByUserId(String userId) {
        User user = this.userDao.get(userId);
        return this.findByRoleId(user.getRoleIds());
    }

    @Override
    public List<Permission> getMenuTreeByUserId(String userId) {
        List<Permission> menus = getPermissionsByUserId(userId).stream().filter(permission ->
                permission.getType() == Permission.Type.MENU).collect(Collectors.toList());

        return toTree(menus);
    }

    @Override
    public List<Permission> getSelectedTreeByRoleId(String roleId) {
        List<Permission> result = this.dao.findAll();
        Role role = this.roleDao.get(roleId);
        if (role != null && StringUtils.isNotBlank(role.getPermissionIds())) {
            String[] permissionIds = role.getPermissionIds().split(",");
            Map<String, String> mapIds = new HashMap<>();
            for (int i = 0; i < permissionIds.length; i++) {
                mapIds.put(permissionIds[i], permissionIds[i]);
            }

            result.forEach(permission -> {
                setPermissionSelected(permission, mapIds);
            });
        }
        return toTree(result);
    }

    @Override
    public Permission getPermissionByUrl(String url) {
        Permission cond = new Permission();
        cond.setPageUrl(url);
        return this.dao.findFirst(cond);
    }

    private void setPermissionSelected(Permission permission, Map<String, String> mapIds) {
        if (mapIds.get(permission.getId()) != null) {
            permission.setSelected(true);
        }

        permission.getChildren().forEach(child -> {
            setPermissionSelected(child, mapIds);
        });
    }

    private List<Permission> toTree(List<Permission> list) {
        return ServiceUtil.sortTree(ServiceUtil.toTree(list));
    }

}